#ifndef EB9DF6F8_9DB6_4B77_84C5_79E403A77DE1
#define EB9DF6F8_9DB6_4B77_84C5_79E403A77DE1

#include <array>
// 模板类和友元
// 模板类声明也可以拥有友元

// 模板类的友元分成三种
// 1. 非模板友元
// 2. 约束模板友元
// 3. 非约束模板友元

// 1. 非模板友元
// 模板类中的非模板友元函数
template <class T>
class HasFriend
{
public:
    friend void counts(); // 上述声明使counts函数成为模板所有实例化的友元

    // 其中友元函数不能通过对象进行调用(因为他不是成员函数), 他可以访问全局对象; 可以使用全局指针访问非全局对象; 可以创建自己的对象; 可以独立与对象的模板类的静态数据成员

    // 如果想要提供一个带有模板参数的友元函数, 需要提供模板参数, 指定一个模板具体化
    friend void counts(HasFriend<int> &);
};

// 2. 约束模板友元
// 可以修改前面的模板, 是友元函数本身成为模板. (为约束模板做准备)

// 首先, 在类定义的前面声明每一个友元函数
template <typename T> void counts();
template <typename T> void report(T &);

// 然后在函数中再次将模板函数声明成友元函数, 这些语句根据模板参数的类型声明具体化
template <typename TT>
class HasFriendT{
    public:
        friend void counts<TT>();
        friend void report<> (HasFriendT<TT> &);
        friend void report<HasFriendT <TT>(HasFriendT<TT> &)
};
// 加入声明了这样的一个对象:
HasFriend <int> hs;
// 那么编译器将会使用int代替TT, 并生成下面的代码
class HasFriend<int> {
    public:
        friend void count<int>();
        friend void report<int> (HasFriend<int> &);
};

// 3. 非约束模板友元函数
// 通过在类内部声明模板, 可以创建非约束友元函数, 即每一个函数具体化都是每个类具体化的友元
template <typename T>
class ManyFriend{
    public:
    template <typename C, typename D> friend void show2(C &, D &);
}

// 这里的具体化匹配非约束模板友元函数
void show2<ManyFriend <int> &, ManyFriend<int> &> (ManyFriend<int> & c, ManyFriend<int> & d);       // 能够访问所有具体化的item成员, 但是他只访问了ManyFriend<int>对象
void show2<ManyFriend <double> &, ManyFriend<int> &> (ManyFriend<double> & c, ManyFriend<int> & d); // 他也是所有ManyFriend具体化的友元, 并访问了ManyFriend<double>和ManyFriend<int>对象


// 模板别名 (c++11)
// 可以使用typedef为模板具体化它提供别名
typedef std::array<double,12> arrd12;
typedef std::array<int,10> arri10;

// C++11 新增了一个特性:  使用模板提供一系列别名 (使用using关键字)
template <typename T>
    using arrtype = std::array<T,12>;       // 创建一系列别名

    arrtype<double> gallons;
    arrtype<int> days;
    arrtype<std::string> months;

// 所有这些机制都是为了让程序员能够重用经过测试的代码, 而不用手工复制他们
// 这样可以简化编程工作, 提高工作效率.

#endif /* EB9DF6F8_9DB6_4B77_84C5_79E403A77DE1 */
